#include <iostream>

#include <list>

#include <boost/foreach.hpp>

#include "c_client.h"
#include "c_server_thread.h"

namespace dhd
{
namespace client
{
   
   ServerThread::ServerThread( const ServerCFG & cfg ) :
       conf(cfg),
       notifier(io_service, boost::bind(&ServerThread::interaction_queued,this)),
       conn(&conf, this, &io_service),
       next_id(1), // 0 is used for 'unsolicited messages'
       have_root(false),
       keep_running(0)
   {
       svr_thread = boost::thread( boost::bind(&ServerThread::run,this) );
   }
   
   
   ServerThread::~ServerThread()
   {
       stop();
   }
   
      
   
   void ServerThread::stop()
   {
       bool join = false;
       
       {
           boost::mutex::scoped_lock guard( svr_mutex );
        
           if (keep_running)
           {
               delete keep_running;
               keep_running = 0;
               io_service.stop();
               join = true;
           }
       }
       
       if (join)
           svr_thread.join();
   }
   
   
   void ServerThread::run()
   {
       {
           boost::mutex::scoped_lock guard( svr_mutex );
           
           if( keep_running )
               return;
           
           keep_running = new boost::asio::io_service::work(io_service);
       }
        
       io_service.run();
   }
   
   
   void ServerThread::interaction_queued()
   {
       if ( have_root )
       {
           boost::mutex::scoped_lock guard( svr_mutex );
           
           while ( !new_interactions.empty() )
           {
               PInteraction pi = new_interactions.front();
               
               send_interaction( pi );
               
               new_interactions.pop_front();
           }
       }
   }


   void ServerThread::send_interaction( PInteraction pi )
   {
       if ( pi->reply_required() )
       {
           if ( pi->request_id == 0 )
           {
               pi->request_id = next_id++;
           
               if (next_id == 0)
                   ++next_id;
           }

           std::cerr << "######### REQUIRE REPLY: " << pi->request_id << std::endl;
           
           pending_replies[ pi->request_id ] = pi;
       }
           
       conn.send_interaction( pi );
   }
   
   
   void ServerThread::queue_interaction( PInteraction & pi )
   {
       if ( pi->reply_error_on_disconnect && !have_root )
       {
           pi->reply.set_msg_type( pi->request.msg_type() );
           pi->reply.set_result( -EFAULT );
           pi->notify_reply_received();
           return;
       }
                   
       {
           boost::mutex::scoped_lock guard( svr_mutex );
           new_interactions.push_back( pi );
       }
       
       notifier.notify();
   }
   
   //------------------------------------------------------------------------------------------------
   
   void ServerThread::got_server_root( const ToClient & reply )
   {
       std::cerr << "################### GOT SERVER ROOT ###################" << std::endl;
       
       if ( reply.msg_type() != DHD_SERVER_ROOT )
           throw BadProtocol("Received unexpected message type in response to DHD_SERVER_ROOT", false);
       
       g_client->del_all( this );
       
       for (int i = 0; i < reply.dir_entries_size(); i++ )
       {
           g_client->add_dir( reply.dir_entries(i).name(),
                              reply.dir_entries(i).mode(),
                              reply.dir_entries(i).inode(),
                              this );
       }
       
       have_root = true;
       
       // Send all queued interactions (if any)
       interaction_queued(); 
   }
       
   //------------------------------------------------------------------------------------------------
   
   void ServerThread::connection_on_connect()
   {
       PInteraction pi = allocate_interaction( true );
       
       pi->request.set_msg_type( dhd::DHD_SERVER_ROOT );
       pi->request.set_path("/");
       
       pi->set_async_complete_callback( boost::bind(&ServerThread::got_server_root, this, _1) );
       
       send_interaction( pi );
   }
   
   
   void ServerThread::connection_on_disconnect()
   {
       std::list< PInteraction > fail_list;
       
       have_root = false;
       
       g_client->del_all( this );

       {
           boost::mutex::scoped_lock guard( svr_mutex );
       
           for (ReplyMap::iterator i; i != pending_replies.end(); i++)
           {
               if ( i->second->reply_error_on_disconnect )
               {
                   i->second->reply.set_msg_type( i->second->request.msg_type() );
                   i->second->reply.set_result( -EFAULT );
                   fail_list.push_back( i->second );
               }
               else
                   new_interactions.push_front( i->second );
           }
       }
       
       pending_replies.clear();
       
       BOOST_FOREACH( PInteraction pi, fail_list )
       {
           pi->notify_reply_received();
       }
   }

   
   void ServerThread::connection_on_reply( PInteraction pi )
   {
       std::cerr << "############### GOT REPLY #################" << std::endl;
       
       ReplyMap::iterator itor = pending_replies.find( pi->request_id );
           
       if ( itor != pending_replies.end() ) 
           pending_replies.erase( itor );
       
       pi->notify_reply_received();
   }
   
   
   
   void ServerThread::connection_on_unsolicited( const ToClient & to_cli )
   {
       switch( to_cli.msg_type() )
       {                      
         case dhd::DHD_INOTIFY_EVENT:
           for (int i=0; i < to_cli.inotify_size(); i++)
           {
               const dhd::INotifyEvent & evt = to_cli.inotify(i);
               
               if ( evt.event_type() == dhd::INOTIFY_CREATE ||
                    evt.event_type() == dhd::INOTIFY_ATTRIB    )
               {
                   g_client->add_dir( evt.name(), evt.mode(), evt.inode(), this );
               } 
               else if ( evt.event_type() == dhd::INOTIFY_DELETE )
               {
                   g_client->del_dir( evt.name() );
               }
               else
                   std::cerr << "INVALID INOTIFY MESSAGE" << std::endl;
               
           }
           break;
       }    
   }
   
   
   
   
}// end namespace dhd::client
}// end namespace dhd

